

/**
 * An Object of type Stats stores the stats for a series of games.
 * Records the number of questions answered correct, incorrect, correct and incorrect 
 * on the first try, correct and incorrect at specific numbers, and the times through a
 * series of questions
 */
public class Stats 
{
	private int correct;
	private int incorrect;
	private int correctOnFirstTry;
	private int incorrectOnFirstTry;
	private int timesPlayed;
	private long averageTime;
	private long fastestTime;
	private DynamicLongArray correctAtNumber;
	private DynamicLongArray incorrectAtNumber;
	private DynamicLongArray averageTimeAtNumber;
	
	/**
	 * Initializes the fields
	 */
	public Stats()
	{
		correct= 0;
		incorrect= 0;
		correctOnFirstTry= 0;
		incorrectOnFirstTry= 0;
		timesPlayed= 0;
		averageTime= 0;
		fastestTime= 0;
		correctAtNumber= new DynamicLongArray();
		incorrectAtNumber= new DynamicLongArray();
		averageTimeAtNumber= new DynamicLongArray();
	}
	
	/**
	 * Takes a SubitizePanel and initializes the fields to equal the panel's
	 * last game.
	 * 
	 * @param source the SubitizePanel to derive the stats from
	 */
	public Stats(SubitizePanel source)
	{
		correct= source.getCorrect();
		incorrect= source.getIncorrect();
		correctOnFirstTry= source.getCorrectOnFirstTry();
		incorrectOnFirstTry= source.getIncorrectOnFirstTry();
		timesPlayed= source.getTimesPlayed();
		averageTime= source.getSessionTimes().average();
		fastestTime= source.getSessionTimes().getMin();
		correctAtNumber= new DynamicLongArray();
		correctAtNumber.add(source.getCorrectAtNumber());
		incorrectAtNumber= new DynamicLongArray();
		incorrectAtNumber.add(source.getIncorrectAtNumber());
		averageTimeAtNumber= new DynamicLongArray();
		averageTimeAtNumber.add(source.getAverageTimeAtNumber());
	}
	
	/**
	 * Takes a SubitizePanel and adds its statistics to the fields.
	 * 
	 * @param addition the SubitizePanel to get more stats from
	 */
	public void addGame(SubitizePanel addition)
	{
		if(addition.getCorrect()==0) return; // game should not count
		correct+= addition.getCorrect();
		incorrect+= addition.getIncorrect();
		correctOnFirstTry+= addition.getCorrectOnFirstTry();
		incorrectOnFirstTry+= addition.getIncorrectOnFirstTry();
		for(int k=0; k<addition.getAverageTimeAtNumber().length; k++)
		{
			if(addition.getCorrectAtNumber()[k]==0)
			{
				continue;
			}
			averageTimeAtNumber.set(k,
					(averageTimeAtNumber.get(k)*correctAtNumber.get(k)+
					 addition.getAverageTimeAtNumber()[k]*addition.getCorrectAtNumber()[k])
					  /(addition.getCorrectAtNumber()[k]+correctAtNumber.get(k)));
		}
		correctAtNumber.merge(addition.getCorrectAtNumber());
		incorrectAtNumber.merge(addition.getIncorrectAtNumber());
		averageTime= (averageTime*timesPlayed+addition.getElapsedTime())
					 /(timesPlayed+1);
		if(addition.getElapsedTime()<fastestTime||fastestTime==0)
		{
			fastestTime= addition.getElapsedTime();
		}
		timesPlayed++;
	}
	
	

	public int getCorrect() 
	{
		return correct;
	}

	public int getIncorrect() 
	{
		return incorrect;
	}

	public int getCorrectOnFirstTry() 
	{
		return correctOnFirstTry;
	}

	public int getIncorrectOnFirstTry() 
	{
		return incorrectOnFirstTry;
	}

	public int getTimesPlayed() 
	{
		return timesPlayed;
	}

	public DynamicLongArray getCorrectAtNumber() 
	{
		return correctAtNumber;
	}

	public DynamicLongArray getIncorrectAtNumber() 
	{
		return incorrectAtNumber;
	}

	public long getAverageTime()
	{
		return averageTime;
	}
	
	public long getFastestTime()
	{
		return fastestTime;
	}
		
	public DynamicLongArray getAverageTimeAtNumber()
	{
		return averageTimeAtNumber;
	}
	
	/**
	 * Returns a String representation of this Stats
	 */
	@Override 
	public String toString()
	{
		String correctString= correct+"c";
		String incorrectString= incorrect+"c";
		String correctOnFirstString= correctOnFirstTry+"c";
		String incorrectOnFirstString= incorrectOnFirstTry+"c";
		String timesPlayedString= timesPlayed+"c";
		String averageTimeString= averageTime+"c";
		String fastestTimeString= fastestTime+"c";
		String correctAtNumberString= correctAtNumber.toString();
		String incorrectAtNumberString= incorrectAtNumber.toString();
		String averageTimeAtNumberString= averageTimeAtNumber.toString();
		return 'h'+correctString+incorrectString+correctOnFirstString+incorrectOnFirstString+
			   timesPlayedString+averageTimeString+fastestTimeString+
			   correctAtNumberString+incorrectAtNumberString+averageTimeAtNumberString+"d";
	}
	
	/**
	 * Reverses toString will create a new Stats form a string.
	 * will throw an IllegalArgumentException if it can't parse the String.
	 * 
	 * @param source the string representation.
	 * @return the new Stats
	 */
	public static Stats fromString(String source)
	{
		char[] chars= source.toCharArray();
		if(chars[0]!='h') return convert(source);
		String next= "";
		String nextDynamic= "";
		Stats temp= new Stats();
		int found= 0;
		int foundDynamic= 0;
		int currIndex= 1;
		for(int k=1; k<chars.length; k++)
		{
			currIndex++;
			if(Character.isDigit(chars[k]))
			{
				next+= chars[k];
			}
			else if(chars[k]=='c')
			{
				int nextNumber= Integer.parseInt(next);
			    next= "";
			    switch(found)
			    {
			    	case 0: temp.correct= nextNumber;
			    			break;
			    	case 1: temp.incorrect= nextNumber;
			    			break;
			    	case 2: temp.correctOnFirstTry= nextNumber;
			    			break;
			    	case 3: temp.incorrectOnFirstTry= nextNumber;
			    			break;
			    	case 4: temp.timesPlayed= nextNumber;
			    			break;
			    	case 5: temp.averageTime= nextNumber;
			    			break;
			    	case 6: temp.fastestTime= nextNumber;
			    			break;
			    	default: throw new IllegalArgumentException("Invaild Data"+found);
			    }  
			    found++;
			    if(found==7) break;
		   }
		}
		for(int i=currIndex; i<chars.length; i++)
		{
			nextDynamic+= chars[i];
			if(chars[i]=='e')
			{
				switch(foundDynamic)
				{
					case 0: temp.correctAtNumber= DynamicLongArray.fromString(nextDynamic);
							break;
					case 1: temp.incorrectAtNumber= DynamicLongArray.fromString(nextDynamic);
							break;
					case 2: temp.averageTimeAtNumber= 
						    DynamicLongArray.fromString(nextDynamic);  
					        break;
					default: throw new IllegalArgumentException("Invaild Data");			
				}
				nextDynamic= "";
				foundDynamic++;
				if(foundDynamic==3) break;
			}
		}
		return temp;
	}
	
	private static Stats convert(String source)
	{
		char[] chars= source.toCharArray();
		DynamicLongArray sessionTimes= new DynamicLongArray();
		String next= "";
		String nextDynamic= "";
		Stats temp= new Stats();
		int found= 0;
		int foundDynamic= 0;
		int currIndex= 0;
		for(int k=0; k<chars.length; k++)
		{
			currIndex++;
			if(Character.isDigit(chars[k]))
			{
				next+= chars[k];
			}
			else if(chars[k]=='c')
			{
				int nextNumber= Integer.parseInt(next);
			    next= "";
			    switch(found)
			    {
			    	case 0: temp.correct= nextNumber;
			    			break;
			    	case 1: temp.incorrect= nextNumber;
			    			break;
			    	case 2: temp.correctOnFirstTry= nextNumber;
			    			break;
			    	case 3: temp.incorrectOnFirstTry= nextNumber;
			    			break;
			    	case 4: temp.timesPlayed= nextNumber;
			    			break;
			    	default: throw new IllegalArgumentException("Invaild Data"+found);
			    }  
			    found++;
			    if(found==5) break;
		   }
		}
		for(int i=currIndex; i<chars.length; i++)
		{
			nextDynamic+= chars[i];
			if(chars[i]=='e')
			{
				switch(foundDynamic)
				{
					case 0: temp.correctAtNumber= DynamicLongArray.fromString(nextDynamic);
							break;
					case 1: temp.incorrectAtNumber= DynamicLongArray.fromString(nextDynamic);
							break;
					case 2: sessionTimes= DynamicLongArray.fromString(nextDynamic);
							break;
					case 3: temp.averageTimeAtNumber= 
				    	    DynamicLongArray.fromString(nextDynamic);  
					        break;
					default: throw new IllegalArgumentException("Invaild Data");			
				}
				nextDynamic= "";
				foundDynamic++;
				if(foundDynamic==4) break;
			}
		}
		temp.averageTime= sessionTimes.average();
		temp.fastestTime= sessionTimes.getMin();
		return temp;
	}
}
